<script>
export default {
  name: "HelloWorld",
  components: {},
  data() {
    return {
      imgUrl: "https://api.shulanfd.com/aist-filesvr-web/api/files/024b89231eea4250960e548141f2e4e0/900_600_t.jpg", //裁剪图片的地址
      imgData: {
        width: 0, //页面中展示的宽高
        height: 0,
        resourceWidth: 0, //图片资源宽高
        resourceHeight: 0,
      },
      // 框选正在移动
      cropMoving: false,
      // 框选正在变化尺寸
      cropResizing: false,
      // 选框数据
      cropData: {
        x: 0,
        y: 0,
        w: 0,
        h: 0,
        mouseX: 0, //鼠标的临时坐标
        mouseY: 0,
        oldW: 0, //上次的宽高
        oldH: 0,
        curIndex: -1, //当前拖拽中的锚点
      },
      config: {
        maxWidth: 550,
        maxHeight: 430,
        // maxWidth: 450,
        // maxHeight: 300,
      },
    };
  },
  mounted() {
    this.getImageSize(this.imgUrl).then(({ width, height }) => {
      // console.log("图片==", width, height);
      this.imgData.resourceWidth = width;
      this.imgData.resourceHeight = height;

      let arr = this.initImageWH(width, height);
      this.imgData.width = arr[0];
      this.imgData.height = arr[1];
    });
  },
  render() {
    let { config, imgUrl, cropData, imgData } = this;
    let { width, height } = imgData;
    let boxStyle = {
      width: config.maxWidth + "px",
      height: config.maxHeight + "px",
    };
    let imgStyle = {
      width: width + "px",
      height: height + "px",
    };

    let hidden = this.cropData.w > 0 || this.cropData.h > 0 ? "" : "hidden";
    let cropStyle = {
      width: cropData.w + "px",
      height: cropData.h + "px",
      left: cropData.x + "px",
      top: cropData.y + "px",
    };
    // <div class="canvas-wrap" style={boxStyle}>
    //       <canvas ref="canvas" width={imgData.resourceWidth} height={imgData.resourceHeight}></canvas>
    //     </div>
    return (
      <div class="wrap">
        <div class="box" ref="cropper" style={boxStyle}>
          <img class="img" style={imgStyle} src={imgUrl} />
          <div class="main" onMousedown={this.handleStartCrop}>
            <div class={`crop ${hidden}`} style={cropStyle} onMousedown={this.handleStartMove}>
              {this.getDragableDoms()}
              <p class="tips">{`x:${cropData.x} y:${cropData.y} width:${cropData.w} height:${cropData.h}`}</p>
            </div>
          </div>
        </div>

        <button onClick={this.handleReset}>重置</button>
      </div>
    );
  },

  methods: {
    drawCanvasContent() {
      const { imgData } = this;
      let { x, y, w, h } = this.getCropInfoRelativeOriginImage();
      let canvas = document.createElement("canvas");
      // let canvas = this.$refs.canvas;
      canvas.width = imgData.resourceWidth;
      canvas.height = imgData.resourceHeight;
      const ctx = canvas.getContext("2d");

      ctx.clearRect(0, 0, imgData.resourceWidth, imgData.resourceHeight);
      ctx.fillStyle = "rgb(0,0,0)";
      ctx.fillRect(0, 0, imgData.resourceWidth, imgData.resourceHeight);

      let img = new Image();
      img.onload = () => {
        // ctx.drawImage(img, 0, 0);

        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect(x, y, w, h);
        let lastMaskImage = canvas.toDataURL();
        console.log(lastMaskImage);
      };
      img.src = this.imgUrl;
    },
    // 获取相对原图选区信息
    getCropInfoRelativeOriginImage() {
      const { imgData, config, cropData } = this;
      // 宽铺满
      let isWidthFull = imgData.width == config.maxWidth;
      let dissX = isWidthFull ? 0 : (config.maxWidth - imgData.width) / 2;
      let dissY = isWidthFull ? (config.maxHeight - imgData.height) / 2 : 0;

      let scale = imgData.resourceWidth / imgData.width;
      let x = cropData.x - dissX;
      let y = cropData.y - dissY;
      let w = cropData.w;
      let h = cropData.h;
      if (x < 0) {
        w = w + x;
        x = 0;
      }
      if (y < 0) {
        h = h + y;
        y = 0;
      }
      return {
        x: Math.floor(x * scale),
        y: Math.floor(y * scale),
        w: Math.floor(w * scale),
        h: Math.floor(h * scale),
      };
    },
    handleReset() {
      this.cropData.w = 0;
      this.cropData.h = 0;
    },
    handleResize(i, e) {
      e.preventDefault();
      this.cropResizing = true;
      this.curIndex = i;
      const { offsetLeft, offsetTop } = this.$refs.cropper;
      const { cropData } = this;
      cropData.mouseX = offsetLeft + cropData.x;
      cropData.mouseY = offsetTop + cropData.y;
      cropData.oldW = cropData.w;
      cropData.oldH = cropData.h;
      window.addEventListener("mousemove", this.resizeCrop);
      window.addEventListener("mouseup", this.endResizeCrop);
    },

    resizeCrop(e) {
      e.preventDefault();

      const { clientX, clientY } = e;
      const { offsetLeft, offsetTop } = this.$refs.cropper;
      const { cropData, config } = this;

      // 横轴向左
      function horizontalLeft() {
        cropData.w = Math.min(cropData.mouseX - clientX + cropData.oldW, cropData.mouseX - offsetLeft + cropData.oldW);
        if (cropData.w > 0) {
          cropData.x = Math.max(clientX - offsetLeft, 0);
        }
      }
      function horizontalRight() {
        cropData.w = Math.min(clientX - offsetLeft - cropData.x, config.maxWidth - cropData.x);
      }
      function verticalTop() {
        cropData.h = Math.min(cropData.mouseY - clientY + cropData.oldH, cropData.mouseY - offsetTop + cropData.oldH);
        if (cropData.h > 0) {
          cropData.y = Math.max(clientY - offsetTop, 0);
        }
      }
      function verticalBottom() {
        cropData.h = Math.min(clientY - offsetTop - cropData.y, config.maxHeight - cropData.y);
      }

      let cb = null;
      switch (Number(this.curIndex)) {
        case 1:
          cb = () => {
            verticalTop();
            horizontalLeft();
          };
          break;
        case 2:
          cb = () => {
            verticalTop();
          };
          break;
        case 3:
          cb = () => {
            horizontalRight();
            verticalTop();
          };
          break;
        case 4:
          cb = () => {
            horizontalLeft();
          };
          break;
        case 5:
          cb = () => {
            horizontalRight();
          };
          break;
        case 6:
          cb = () => {
            horizontalLeft();
            verticalBottom();
          };
          break;

        case 7:
          cb = () => {
            verticalBottom();
          };
          break;

        case 8:
          cb = () => {
            horizontalRight();
            verticalBottom();
          };
          break;
      }

      this.$nextTick(cb);
    },

    endResizeCrop() {
      this.cropResizing = false;
      window.removeEventListener("mousemove", this.resizeCrop);
      window.removeEventListener("mouseup", this.endResizeCrop);

      this.drawCanvasContent();
    },

    getDragableDoms() {
      let list = ["1", "2", "3", "4", "5", "6", "7", "8"];
      return list.map((item) => {
        return <span class={`crop-point point${item}`} onMousedown={this.handleResize.bind(this, item)}></span>;
      });
    },
    // 鼠标按钮开始移动选区
    handleStartMove(e) {
      e.preventDefault();
      if (this.cropResizing) return;
      // console.log("handleStartMove");
      this.cropMoving = true;
      window.addEventListener("mousemove", this.moveCrop);
      window.addEventListener("mouseup", this.endMoveCrop);

      this.cropData.mouseX = e.offsetX;
      this.cropData.mouseY = e.offsetY;
    },

    moveCrop(e) {
      e.preventDefault();
      const { clientX, clientY } = e;
      const { offsetLeft, offsetTop } = this.$refs.cropper;
      const { cropData, config } = this;

      this.$nextTick(() => {
        cropData.x = Math.min(Math.max(clientX - offsetLeft - cropData.mouseX, 0), config.maxWidth - cropData.w);
        cropData.y = Math.min(Math.max(clientY - offsetTop - cropData.mouseY, 0), config.maxHeight - cropData.h);
      });
    },

    endMoveCrop() {
      this.cropMoving = false;

      window.removeEventListener("mousemove", this.moveCrop);
      window.removeEventListener("mouseup", this.endMoveCrop);

      this.drawCanvasContent();
    },

    // 鼠标按下开始建立选区
    handleStartCrop(e) {
      e.preventDefault();
      // console.log("handleStartCrop");
      if (this.cropMoving || this.cropResizing) return;

      const { cropData } = this;
      if (cropData.w > 0 || cropData.h > 0) return;

      window.addEventListener("mousemove", this.createCrop);
      window.addEventListener("mouseup", this.endCreateCrop);

      cropData.x = e.offsetX;
      cropData.y = e.offsetY;
      cropData.w = 0;
      cropData.h = 0;
    },

    createCrop(e) {
      e.preventDefault();

      const { clientX, clientY } = e;
      const { offsetLeft, offsetTop } = this.$refs.cropper;
      const { cropData, config } = this;

      this.$nextTick(() => {
        this.cropData.w = Math.min(clientX - offsetLeft - cropData.x, config.maxWidth - cropData.x);
        this.cropData.h = Math.min(clientY - offsetTop - cropData.y, config.maxHeight - cropData.y);
      });
    },
    endCreateCrop(e) {
      e.preventDefault();

      window.removeEventListener("mousemove", this.createCrop);
      window.removeEventListener("mouseup", this.endCreateCrop);

      this.drawCanvasContent();
    },
    initImageWH(imgW, imgH) {
      let w = this.config.maxWidth;
      let h = this.config.maxHeight;
      let percent = imgW / imgH;

      function mainWidth() {
        imgW = w;
        imgH = imgW / percent;
      }

      function mainHeight() {
        imgH = h;
        imgW = imgH * percent;
      }

      let bool = w / h > imgW / imgH;
      if (bool) {
        mainHeight();
      } else {
        mainWidth();
      }

      return [imgW, imgH];
    },
    getImageSize(src) {
      return new Promise((resolve) => {
        let img = new Image();
        img.onload = () => {
          img.onload = null;
          resolve({
            width: img.width,
            height: img.height,
          });
        };
        img.src = src;
      });
    },
  },
};
</script>

<style scoped>
.wrap {
  width: 100%;
  height: 100vh;
  /* display: flex; */
}
.box {
  /* margin: auto; */
  background-image: url("");
  position: relative;
  border: 2px solid black;
  /* overflow: hidden; */
}
.img {
  opacity: 1;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
.crop {
  position: absolute;
  border: 1px solid rgb(80, 191, 255);
  cursor: move;
  background-color: rgba(80, 291, 255, 0.2);
}
.crop.hidden {
  visibility: hidden;
}
.main {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
}
.crop-point {
  position: absolute;
  width: 8px;
  height: 8px;
  opacity: 0.75;
  background-color: #39f;
}

.crop-point.point1 {
  top: -4px;
  left: -4px;
  cursor: nw-resize;
}
.crop-point.point2 {
  top: -5px;
  left: 50%;
  margin-left: -3px;
  cursor: n-resize;
}
.crop-point.point3 {
  top: -4px;
  right: -4px;
  cursor: ne-resize;
}
.crop-point.point4 {
  top: 50%;
  left: -4px;
  margin-top: -3px;
  cursor: w-resize;
}
.crop-point.point5 {
  top: 50%;
  right: -4px;
  margin-top: -3px;
  cursor: e-resize;
}
.crop-point.point6 {
  bottom: -5px;
  left: -4px;
  cursor: sw-resize;
}
.crop-point.point7 {
  bottom: -5px;
  left: 50%;
  margin-left: -3px;
  cursor: s-resize;
}
.crop-point.point8 {
  bottom: -5px;
  right: -4px;
  cursor: se-resize;
}
.tips {
  font-size: 12px;
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  position: absolute;
  top: -20px;
  width: 200px;
}
.canvas-wrap {
  /* margin: auto; */
  /* border: 2px solid palevioletred; */
}
</style>
